package com.elitel.frame.business.service.impl;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

import com.alibaba.fastjson.JSON;
import com.elitel.base.variable.RabbitMQRoutingKey;
import com.elitel.common.businessJdbc.BusinessSelectPreparedStatement;
import com.elitel.common.businessJdbc.BusinessSelectPreparedStatementHandler;
import com.elitel.common.exception.SingleModeMismatchException;
import com.elitel.common.utils.XCommonUtils;
import com.elitel.frame.base.component.RedisCacheCfgMaster;
import com.elitel.frame.base.component.RedisCacheComponent;
import com.elitel.frame.base.service.BaseFieldaddonsService;
import com.elitel.frame.base.service.BaseMasterdetailService;
import com.elitel.frame.base.service.BaseShowfieldService;
import com.elitel.frame.base.service.CfgTableService;
import com.elitel.frame.business.dao.ext.ExtMasterMapper;
import com.elitel.frame.business.service.ExtMasterService;
import com.elitel.frame.business.service.TableFieldCommService;
import com.elitel.frame.business.service.utils.SQLConstructHelper;
import com.elitel.frame.main.dao.ext.ExtBaseMasterdetailMapper;
import com.elitel.frame.main.dao.ext.ExtCfgFieldMapper;
import com.elitel.frame.main.dao.ext.ExtCfgTableMapper;
import com.elitel.frame.main.entity.BaseFieldaddons;
import com.elitel.frame.main.entity.CfgDb;
import com.elitel.frame.main.entity.CfgExtmaster;
import com.elitel.frame.main.entity.CfgTable;
import com.elitel.frame.main.entity.CfgTableFieldCustomAlias;
import com.elitel.frame.main.entity.constant.DictionaryConstant;
import com.elitel.frame.main.entity.ext.BaseFieldaddonsExt;
import com.elitel.frame.main.entity.ext.BaseMasterdetailExt;
import com.elitel.frame.main.entity.ext.BaseQueryfieldExt;
import com.elitel.frame.main.entity.state.TimePushActivemq;
import com.elitel.frame.main.entity.vo.ConQueryVo;
import com.elitel.frame.main.entity.vo.PageResponseVo;
import com.elitel.frame.main.entity.vo.ServerStatistics;

@Service
@Slf4j
public class ExtMasterServiceImpl implements ExtMasterService{
	
	@Autowired
	private RedisCacheComponent redisCacheComponent;
	@Autowired
	private ExtCfgTableMapper extCfgTableMapper;
	@Autowired
	private ExtCfgFieldMapper extCfgFieldMapper;
	@Autowired
	private ExtMasterMapper extMasterMapper;
	@Autowired
	private RedisCacheCfgMaster redisCacheCfgMaster;
	@Autowired
	private ExtBaseMasterdetailMapper extBaseMasterdetailMapper;
	@Autowired
	private TableFieldCommService tableFieldCommService;
	@Autowired
	private BaseMasterdetailService baseMasterdetailService;
	@Autowired
	private BaseFieldaddonsService baseFieldaddonsService;

	@Autowired
	private BaseShowfieldService baseShowFieldService;

	@Autowired
	private CfgTableService cfgTableService;

    @Autowired
    private BusinessSelectPreparedStatementHandler businessPrepareStatementHandler;

	@Override
	public PageResponseVo queryMasterTablePagination(CfgDb cfgDb, CfgExtmaster extmaster, String pageNO, String pageSize, String conQuery) {
		//long serverStartTime=System.currentTimeMillis();
		PageResponseVo<Map<String, Object>> pageResponse = new PageResponseVo<Map<String, Object>>();
		List<Map<String, Object>> masterObjectMapList = null;
		CfgTable cfgTable = null;
		long count;

		if(extmaster == null)
			return pageResponse;

		cfgTable =  redisCacheComponent.cfgTableRedisCache(extmaster.getDtConfigId(), extmaster.getDbKey());
		if(cfgTable == null)
			return pageResponse;

		//条件的集合
		Map passConditionMap = null;
		if(XCommonUtils.isNotEmpty(conQuery)) {
			passConditionMap =(Map)JSON.parse(conQuery);
		}

		String sort = "";
		String tables = "";
		String fields="";
		String whereSql = "";

		//设置表名称
		tables = tableFieldCommService.getTablenameTxt(cfgDb, extmaster.getDtConfigId());
		//设置显示字段
		fields = tableFieldCommService.getFieldnameByserviceID(cfgDb,extmaster.getGuid(),
															   DictionaryConstant.ServiceType_MasterSlaveTableService_String,
															   extmaster.getDtConfigId(),
															   DictionaryConstant.FindMasterTable);

		//设置参数条件
		String sqltxt = tableFieldCommService.getQuerysqlByserviceId(cfgDb, extmaster.getGuid(), DictionaryConstant.MasterSlaveTableServiceInBaseQuerySql,"");
		String whereContent = tableFieldCommService.processSqlByFreemarker(cfgDb, sqltxt, passConditionMap);
		if(XCommonUtils.isNotEmpty(whereContent)){
			if(XCommonUtils.isViewTableType(cfgTable.getDtTabletype()))
				whereSql = handleViewWhereCondition(extmaster, whereSql, whereContent);
			else
				whereSql += whereContent;
		}

		//设置排序字段
		if(XCommonUtils.isNotEmpty(cfgTable.getDtOrder())){
			sort =" order by "+cfgTable.getDtOrder();
		}

        // here is get queryField object
        List<BaseQueryfieldExt> baseQueryFieldExtList = tableFieldCommService.getQueryFieldObjectByServiceIdAndServiceType(cfgDb, extmaster.getGuid(), DictionaryConstant.ServiceType_MasterSlaveTableService_String);

        Map<String, String> fieldTypeMap = new HashMap<>();
        if(baseQueryFieldExtList != null){
            baseQueryFieldExtList.forEach(baseQueryFieldExt -> fieldTypeMap.put(baseQueryFieldExt.getFieldname().trim(), baseQueryFieldExt.getFieldtype().trim()));
        }


        BusinessSelectPreparedStatement bps = BusinessSelectPreparedStatement.builder(cfgDb.getDbVersion())
                                        .serviceGuid(extmaster.getGuid())
                                        .fieldSection(fields)
                                        .tableSection(tables)
                                        .conditionSection(whereSql)
                                        .orderSection(sort)
                                        .fieldTypeMap(fieldTypeMap)
                                        .passCondition(passConditionMap)
                                        .isPageable(XCommonUtils.isPageable(extmaster.getIspagination()))
                                        .pageNumber(pageNO)
                                        .pageSize(pageSize)
                                        .build();


        masterObjectMapList = businessPrepareStatementHandler.findRowListOfFieldResultByExecuteSql(bps);
        count = businessPrepareStatementHandler.rowCountByExecuteSql(bps);

		// here is find first element to find relative information for slave table
		// here is map object into slave table
		addRelationSlaveTableInfoForFirstElement(masterObjectMapList, cfgTable, extmaster, cfgDb);

		pageResponse.setTotal((int)count);
		pageResponse.setRows(masterObjectMapList);
		return pageResponse;
	}

	private void addRelationSlaveTableInfoForFirstElement(List<Map<String, Object>> masterObjectMapList, CfgTable cfgTable, CfgExtmaster extmaster, CfgDb cfgDb){
		if(masterObjectMapList == null || masterObjectMapList.size() <1)
			return;

		// here get first element
		// find relative information for first element
		List<BaseMasterdetailExt> slaveList = redisCacheCfgMaster.getMasterdetailbyMguid(extmaster.getGuid());
		if(XCommonUtils.isEmpty(slaveList))
			return;

		List<CfgTableFieldCustomAlias> primaryKeyCustomAlias = baseShowFieldService.getCfgTablePrimaryKeyCustomAliasByServiceId(cfgTable.getDtTabletype(), extmaster.getGuid());

		// handle multiple line mode
		Map<String, Object> masterObjectMap  = masterObjectMapList.get(0);

		// here is deep clone map for object
		String jsonObject = JSON.toJSONString(masterObjectMap);
		Map<String, Object> masterObjectMapClone = JSON.parseObject(jsonObject, Map.class);
		// handle masterObjectMap key , if existed customAlias , replace into origin column
		convertCustomAliasToOriginFieldName(primaryKeyCustomAlias, masterObjectMapClone);
		Map<String, Object> slaveTableMap = handleSlaveTableData(cfgDb, extmaster, masterObjectMapClone, slaveList, cfgTable);
		if(XCommonUtils.isEmpty(slaveTableMap))
			return;

		for(Map.Entry<String, Object> slaveTable : slaveTableMap.entrySet()){
			masterObjectMap.put(slaveTable.getKey(), slaveTable.getValue());
		}

	}

	private String handleViewWhereCondition(CfgExtmaster extmaster, String whereSql, String whereContent) {
		return SQLConstructHelper.handleViewWhereCondition(baseFieldaddonsService, extmaster.getDtConfigId(), whereContent);
	}


	@Override
	public Object querySlaveTBList(CfgDb cfgDb, CfgExtmaster extmaster, String primaryKeyValueArrayByComma) {
		HashMap<String,Object> resultMap = new LinkedHashMap<String,Object>();
		CfgTable master = extCfgTableMapper.queryCfgTable(extmaster.getDtConfigId());
		if(master ==null)
			return resultMap;

		String masterSql = handleMasterTableSql(cfgDb, extmaster, primaryKeyValueArrayByComma, master);

		List<Map<String, Object>> masterObjList = extMasterMapper.querySlaveTBList(masterSql);
		if(!conformResponseMode(extmaster, masterObjList)){
			throw new SingleModeMismatchException("In SingleMode , but existed mutliple row data");
		}

		List<BaseMasterdetailExt> slaveList = redisCacheCfgMaster.getMasterdetailbyMguid(extmaster.getGuid());
		if(XCommonUtils.isEmpty(slaveList))
			return resultMap;

		CfgTable cfgTable = cfgTableService.getInfoByDtconfigId(extmaster.getDtConfigId());

		List<CfgTableFieldCustomAlias> primaryKeyCustomAlias = baseShowFieldService.getCfgTablePrimaryKeyCustomAliasByServiceId(cfgTable.getDtTabletype(), extmaster.getGuid());

		// handle multiple line mode
		List<Map<String, Object>> resultMapList = new LinkedList<>();
		for(Map<String, Object> masterObjectMap : masterObjList ){
			// handle masterObjectMap key , if existed customAlias , replace into origin column
			convertCustomAliasToOriginFieldName(primaryKeyCustomAlias, masterObjectMap);
			resultMapList.add(handleSlaveTableData(cfgDb, extmaster, masterObjectMap, slaveList, cfgTable));
		}

		return resultMapList;
	}

	private void convertCustomAliasToOriginFieldName(List<CfgTableFieldCustomAlias> fieldCustomAliasList, Map<String, Object> masterObjectMap){
		if(fieldCustomAliasList == null)
			return;

		for(CfgTableFieldCustomAlias fieldCustomAlias : fieldCustomAliasList){
			Object value = masterObjectMap.get(fieldCustomAlias.getCustomAlias());
			if(value == null)
				continue;

			masterObjectMap.remove(fieldCustomAlias.getCustomAlias());
			masterObjectMap.put(fieldCustomAlias.getFieldName(), value);
		}
	}

	private boolean conformResponseMode(CfgExtmaster extMaster, List<Map<String, Object>> masterObjectList){
		if(XCommonUtils.isMultipleResponseMode(extMaster))
			return true;

		return singleMode(extMaster) && masterObjectList.size() <=1;
	}

	private boolean singleMode(CfgExtmaster extMaster){
		return XCommonUtils.isSingleResponseMode(extMaster);
	}

   private HashMap<String, Object> handleSlaveTableData(CfgDb cfgDb, CfgExtmaster extmaster, Map<String, Object> masterObjectMap, List<BaseMasterdetailExt> slaveList, CfgTable masterTable) {
	   HashMap<String, Object> resultMap = new LinkedHashMap<String, Object>();


	   String metaTableAlias = "AA.";
	   String masterTableAlterName = metaTableAlias; //alter name of master table

      for (BaseMasterdetailExt baseMasterdetailExt : slaveList) {

			if(XCommonUtils.isViewTableType(masterTable.getDtTabletype()) && masterTableAlterName.equals(metaTableAlias)){ // only find once is enough
				// here can find master table alter Name , because can find master field
				masterTableAlterName = getMasterTableAlterName(masterTable, baseMasterdetailExt);
			}

			//设置表名称
			String tables = tableFieldCommService.getTablenameTxt(cfgDb, baseMasterdetailExt.getDtConfigId());
			//设置显示字段
			String fields = tableFieldCommService.getFieldnameByserviceID(cfgDb, extmaster.getGuid(), DictionaryConstant.MasterSlaveTableServiceInBaseQuerySql, baseMasterdetailExt.getDtConfigId(),baseMasterdetailExt.getGuid());
			if(XCommonUtils.isEmpty(fields)){
				log.warn("Don't set show field for slave table service, extmaster name = " + extmaster.getServicename());
				continue;
			}

			//组装where条件语句
			String whereSql = " ";
			if(XCommonUtils.isNotEmpty(masterObjectMap)){
				for (String ms : masterObjectMap.keySet()) {
					if(masterTableAlterName != null && ms.equals(masterTableAlterName + "_" + baseMasterdetailExt.getMfieldname())){
						// slave table is view
						whereSql += " and " + baseMasterdetailExt.getAltertablename()+"." + baseMasterdetailExt.getDfieldname() + " = '" + masterObjectMap.get(ms).toString()+"'";
						break;

					} else if (ms.equals(baseMasterdetailExt.getMfieldname())){
						// slave table is origin table
						whereSql += " and " + baseMasterdetailExt.getAltertablename()+"." + baseMasterdetailExt.getDfieldname() + " = '" + masterObjectMap.get(ms).toString()+"'";
						break;
					}
				}
			}

			String sql = "select " + fields + " from " + tables + " where 1 = 1 " + whereSql;
			List<Map<String, Object>> querySlaveTBList = extMasterMapper.querySlaveTBList(sql);

			String tableName = XCommonUtils.isEmpty(baseMasterdetailExt.getSlaveTableCustomAlias()) ? baseMasterdetailExt.getDtConfigId() : baseMasterdetailExt.getSlaveTableCustomAlias();
            resultMap.put(tableName, querySlaveTBList);
		}

	  return resultMap;
   }

private String getMasterTableAlterName(CfgTable masterTable, BaseMasterdetailExt baseMasterdetailExt) {
    String masterTableAlterName;
    List<BaseFieldaddons> baseFieldaddonsList = baseFieldaddonsService.selectFieldaddonsBydtconfigid(masterTable.getDtConfigId());
    Map<String, List<BaseFieldaddons>> baseFieldaddonsListMap = baseFieldaddonsList.stream().collect(Collectors.groupingBy(BaseFieldaddons :: getGuid));
    List<BaseFieldaddons> baseFieldaddonsListByMguid = baseFieldaddonsListMap.get(baseMasterdetailExt.getMfield());
	masterTableAlterName = baseFieldaddonsListByMguid.get(0).getAltername();
    return masterTableAlterName;
}

  private String handleMasterTableSql(CfgDb cfgDb, CfgExtmaster extmaster, String primaryKeyValueArray, CfgTable master) {
	    //设置主表名称
		String mastertables = tableFieldCommService.getTablenameTxt(cfgDb, extmaster.getDtConfigId());
		//设置主表显示字段
		String masterfields = tableFieldCommService.getFieldnameByserviceID(cfgDb, extmaster.getGuid(),"2", extmaster.getDtConfigId(),"");
		//设置主表条件
		String masterWhere = handleMasterWhereCondition(cfgDb, primaryKeyValueArray, master);

		return "select " + masterfields + " from " + mastertables + masterWhere;
  }

  private String handleMasterWhereCondition(CfgDb cfgDb, String primaryKeyValueArray,  CfgTable master) {
	  return tableFieldCommService.handleWhereConditionByTableType(cfgDb, primaryKeyValueArray, master);
  }

}
